/*
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! CHECKER      Common JIT profiling
//! RUN          options: "--no-async-jit --compiler-hotness-threshold=10 --compiler-regex _GLOBAL::test_.*", entry: "_GLOBAL::func_main_0"
//! EVENT_NOT    /Deoptimization.*/
//!
//! METHOD       "test_profile"
//! PASS_AFTER   "IrBuilder"
//! INST         /Intrinsic.Add.*/
//! INST         /Intrinsic.Neg.*/
//! PASS_AFTER   "Codegen"
//! INST_NOT     /Intrinsic.Add.*/
//! INST_NOT     /Intrinsic.Neg.*/
//!
//! METHOD       "test_update"
//! PASS_AFTER   "IrBuilder"
//! INST         /Intrinsic.Or.*/
//! PASS_AFTER   "Codegen"
//! INST_NOT     /Intrinsic.Or.*/
//! INST         "AnyTypeCheck ECMASCRIPT_DOUBLE_TYPE i s"
//! INST_NEXT    "CastAnyTypeValue ECMASCRIPT_DOUBLE_TYPE i s"
//! INST_NEXT    "Cast"
//! INST_NEXT    "Or"
//!
//! METHOD       "test_eq"
//! PASS_AFTER   "Codegen"
//! INST         /Intrinsic.EqDyn.*/
//!
//! METHOD       "test_strict_eq"
//! PASS_AFTER   "Codegen"
//! INST         /Intrinsic.StrictEqDyn.*/
//!
//! METHOD       "test_strict_eq_number"
//! PASS_AFTER   "IrBuilder"
//! INST         /Intrinsic.StrictEq.*/
//! PASS_AFTER   "Codegen"
//! INST_NOT     /Intrinsic.StrictEq.*/
//! INST         "AnyTypeCheck ECMASCRIPT_DOUBLE_TYPE i p"
//! INST_NEXT    "CastAnyTypeValue ECMASCRIPT_DOUBLE_TYPE i"
//! INST_NEXT    "Cast"
//! INST_NEXT    "Compare EQ f64"
//!
//! METHOD       "test_special_int"
//! PASS_AFTER   "IrBuilder"
//! INST         /Intrinsic.Add.*/
//! PASS_AFTER   "Codegen"
//! INST_NOT     /Intrinsic.Add.*/
//! INST_COUNT   "AnyTypeCheck ECMASCRIPT_INT_TYPE s", 2
//! INST_COUNT   "CastAnyTypeValue ECMASCRIPT_INT_TYPE s", 2
//! INST         "Add"
//!
//! METHOD       "test_special_all"
//! PASS_AFTER   "IrBuilder"
//! INST         /Intrinsic.Sub.*/
//! PASS_AFTER   "Codegen"
//! INST_NOT     /Intrinsic.Sub.*/
//! INST_COUNT   "AnyTypeCheck ECMASCRIPT_DOUBLE_TYPE i s", 2
//! INST_COUNT   "CastAnyTypeValue ECMASCRIPT_DOUBLE_TYPE i s", 2
//! INST         "Sub"

function test_profile(a, b) {
    a = a + b;
    return -a;
}

var res = 0;
for (var i = 0; i < 20; i++) {
    res += test_profile(i, res);
}

if (res != -19) {
    throw "Wrong result: " + res;
}

var arr = new Array(5);
function test_update(idx, val) {
    arr[idx] |= val;
}

// Initializing the array
test_update(4, 0)
test_update(3, 0)
test_update(2, 0)
test_update(1, 0)
test_update(0, 0)

for (var i = 0; i < 20; i++) {
    test_update(i >> 2, 1 << i);
}
var sum = 0;
for (var i = 0; i < 5; i++) {
    sum += arr[i];
}
if (sum != (1 << 20) - 1) {
    throw "Wrong array sum: " + sum;
}

// Profiled input types can be cast to double, but don't inline because
// undefined == undefined and NaN != NaN
function test_eq(x, y) {
    return x == y;
}

sum = 0;
for (var i = 0; i < 5; i++) {
    sum += test_eq(undefined, undefined);
    sum += test_eq(NaN, NaN);
    sum += test_eq(NaN, undefined);
    sum += test_eq(1.1, 1.1);
}
if (sum != 10) {
    throw "Wrong sum for equals: " + sum;
}

// Profiled input types can be cast to int, but dont inline because i. e. false !== 0
function test_strict_eq(x, y) {
    return x === y;
}

sum = 0;
for (var i = 0; i < 5; i++) {
    sum += test_strict_eq(false, null);
    sum += test_strict_eq(true, 1);
    sum += test_strict_eq(0, false);
    sum += test_strict_eq(1, 1);
}
if (sum != 5) {
    throw "Wrong sum for strict equals: " + sum;
}

// Profiled input types are int or double, inline
function test_strict_eq_number(x, y) {
    return x === y;
}

sum = 0;
for (var i = 1; i <= 5; i++) {
    sum += test_strict_eq_number(i, i / 3);
    sum += test_strict_eq_number(i, i);
    sum += test_strict_eq_number(1 / i, 1 / i);
    sum += test_strict_eq_number(1 / i * i, 1);
}
if (sum != 15) {
    throw "Wrong sum for strict equals with number inputs: " + sum;
}

function test_special_int(x, y) {
    return x + y;
}

for (var i = 0; i < 5; i++) {
    let result = test_special_int(i, i < 10);
    if (result != i + 1) {
        throw "Wrong result for int + true: " + result;
    }
    result = test_special_int(10, i < 0);
    if (result != 10) {
        throw "Wrong result for int + false: " + result;
    }
    result = test_special_int(null, i);
    if (result != i) {
        throw "Wrong result for null + int: " + result;
    }
}

function test_special_all(x, y) {
    return x - y;
}

for (var i = 0; i < 5; i++) {
    let result = test_special_all(undefined, 1.1);
    if (result == result) {
        throw "Wrong result for undefined + double: " + result;
    }
    result = test_special_all(2.5, true);
    if (result != 1.5) {
        throw "Wrong result for double + true: " + result;
    }
    result = test_special_all(i * 2, i);
    if (result != i) {
        throw "Wrong result for int + int profiled as double:" + result;
    }
    result = test_special_all(null, false);
    if (result != 0) {
        throw "Wrong result for null + false profiled as double:" + result;
    }
}